package com.wzcl.app.service.impl;

import com.alibaba.fastjson.JSON;
import com.wzcl.app.dao.model.*;
import com.wzcl.app.dao.repository.LiveRoomRepository;
import com.wzcl.app.model.common.BusinessException;
import com.wzcl.app.model.common.CodeMsg;
import com.wzcl.app.model.common.MyPage;
import com.wzcl.app.model.common.WZCL;
import com.wzcl.app.model.dto.LiveRoomDto;
import com.wzcl.app.service.*;
import com.wzcl.app.service.common.PolyvService;
import com.wzcl.app.utils.RedisUtil;
import lombok.extern.log4j.Log4j2;
import net.polyv.live.bean.result.channel.Playback;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.text.SimpleDateFormat;
import java.util.*;

@Log4j2
@Service
public class LiveRoomServiceImpl implements LiveRoomService {
    public static final String STATE_KEY = "LIVE_START_";
    public static final String END_KEY = "LIVE_END_";

    @Autowired
    private LiveRoomRepository roomRepository;

    @Autowired
    private CourseService courseService;

    @Autowired
    private ChannelService channelService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserCourseService userCourseService;

    @Autowired
    private UserService userService;

    @Autowired
    private PolyvService polyvService;

    @Override
    @Transactional
    public LiveRoom addLiveRoom(LiveRoomDto liveRoomDto) {
        //先校验时间的合法性
        long now = System.currentTimeMillis() / 1000;
        long start = liveRoomDto.getStartTime().getTime() / 1000;
        long end = liveRoomDto.getEndTime().getTime() / 1000;
        //开始时间不小于当前&&直播时长不小于三分钟
        if (start < now || start > end - (3 * 60)) {
            throw new BusinessException(CodeMsg.ROOM_OPERATE_FAIL, "直播时间不符合规范");
        }
        int startSecond = (int) (start - now);
        int endSecond = (int) (end - now);
        //获取课程
        Course course = courseService.getAndCheckById(liveRoomDto.getCourseId());
        //创建
        LiveRoom liveRoom = new LiveRoom();
        String roomNub = createRoomNub();
        liveRoom.setNub(roomNub);
        liveRoom.setCourseId(course.getId());
        liveRoom.setName(liveRoomDto.getName());
        liveRoom.setStartTime(liveRoomDto.getStartTime());
        liveRoom.setEndTime(liveRoomDto.getEndTime());
        liveRoom.setTeacherPhone(liveRoomDto.getTeacherPhone());
        liveRoom.setModified(true);
        Channel channel = channelService.takeAvailable(liveRoomDto.getBasicSetting(), liveRoomDto.getAuthSetting());
        liveRoom.setChannelId(channel.getChannelId());
        if (startSecond == 0) {
            liveRoom.setRoomState(WZCL.ROOM_STATE.ON);
        } else {
            liveRoom.setRoomState(WZCL.ROOM_STATE.OFF);
            redisUtil.set(STATE_KEY + roomNub, roomNub, startSecond);
        }
        redisUtil.set(END_KEY + roomNub, roomNub, endSecond);
        return roomRepository.save(liveRoom);
    }

    //单纯修改直播间名称
    @Override
    public LiveRoom updateLiveRoomName(LiveRoomDto liveRoomDto) {
        //校验获得
        LiveRoom room = getAndCheckByNub(liveRoomDto.getNub());
        //修改判断
        if (!room.getModified()) {
            throw new BusinessException(CodeMsg.ROOM_OPERATE_FAIL, "当前直播记录无法修改");
        }
        if (StringUtils.isNotBlank(liveRoomDto.getName())) {
            room.setName(liveRoomDto.getName());
            //同时修改所在频道的名字
            polyvService.setChannelName(room.getChannelId(), liveRoomDto.getName());
            return roomRepository.save(room);
        }
        return room;
    }


    @Override
    public LiveRoom setTeacher(LiveRoomDto liveRoomDto) {
        if (StringUtils.isBlank(liveRoomDto.getTeacherPhone())) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少用户手机号");
        }
        //校验获得
        LiveRoom room = getAndCheckByNub(liveRoomDto.getNub());
        //设置老师
        room.setTeacherPhone(liveRoomDto.getTeacherPhone());
        return roomRepository.save(room);
    }


    //修改直播间时间
    @Override
    public LiveRoom updateLiveRoomTime(LiveRoomDto liveRoomDto) {
        //校验获得
        LiveRoom room = getAndCheckByNub(liveRoomDto.getNub());
        //能否修改的标识
        if (!room.getModified()) {
            throw new BusinessException(CodeMsg.ROOM_OPERATE_FAIL, "当前直播记录无法修改");
        }
        long now = System.currentTimeMillis() / 1000;
        //开始时间不小于当前&&直播时长不小于三分钟
        if (liveRoomDto.getStartTime() != null) {
            if (!WZCL.ROOM_STATE.OFF.equals(room.getRoomState())) {
                throw new BusinessException(CodeMsg.ROOM_OPERATE_FAIL, "无法修改开播时间");
            }
            long start = liveRoomDto.getStartTime().getTime() / 1000;
            int startSecond = (int) (start - now);
            room.setStartTime(liveRoomDto.getStartTime());
            if (startSecond == 0) {
                room.setRoomState(WZCL.ROOM_STATE.ON);
            } else {
                room.setRoomState(WZCL.ROOM_STATE.OFF);
                redisUtil.set(STATE_KEY + room.getNub(), room.getNub(), startSecond);
            }
        }
        if (liveRoomDto.getEndTime() != null) {
            long end = liveRoomDto.getEndTime().getTime() / 1000;
            int endSecond = (int) (end - now);
            room.setEndTime(liveRoomDto.getEndTime());
            redisUtil.del(END_KEY + room.getNub());
            if (endSecond == 0) {
                //立即关闭
                if (redisUtil.hasKey(STATE_KEY + room.getNub())) {
                    redisUtil.del(STATE_KEY + room.getNub());
                }
                room.setRoomState(WZCL.ROOM_STATE.OFF);
                room.setModified(false);
                channelService.putAvailableByChannelId(room.getChannelId());
            } else {
                redisUtil.set(END_KEY + room.getNub(), room.getNub(), endSecond);
            }
        }
        return roomRepository.save(room);
    }

    @Override
    public LiveRoom delLiveRoom(String roomNub) {
        //校验获取
        LiveRoom room = getAndCheckByNub(roomNub);
        //能否修改的标识
        if (room.getModified()) {
            throw new BusinessException(CodeMsg.ROOM_OPERATE_FAIL, "无法删除未结束的直播记录");
        }
        log.info("LiveRoom:{} Physical deletion", JSON.toJSONString(room));
        roomRepository.delete(room);
        return room;
    }

    @Override
    public LiveRoom getAndCheckByNub(String roomNub) {
        if (StringUtils.isBlank(roomNub)) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少直播间编号");
        }
        LiveRoom room = roomRepository.findByNub(roomNub);
        if (room == null) {
            throw new BusinessException(CodeMsg.ROOM_NOT_EXIST);
        }
        return room;
    }


    @Override
    public void onLive(String roomNub) {
        LiveRoom room = roomRepository.findByNub(roomNub);
        room.setRoomState(WZCL.ROOM_STATE.ON);
        roomRepository.save(room);
        log.info("{} Room onLive!", roomNub);
    }

    @Override
    public void offLive(String roomNub) {
        LiveRoom room = roomRepository.findByNub(roomNub);
        room.setRoomState(WZCL.ROOM_STATE.OFF);
        room.setModified(false);
        //归还频道资源
        channelService.putAvailableByChannelId(room.getChannelId());
        //

        roomRepository.save(room);
        log.info("{} Room offLive!", roomNub);
    }

    @Override
    public Channel goLive(LiveRoomDto liveRoomDto) {
        //校验获得
        LiveRoom room = getAndCheckByNub(liveRoomDto.getNub());
        //状态校验
        if (!WZCL.ROOM_STATE.ON.equals(room.getRoomState())) {
            throw new BusinessException(CodeMsg.ROOM_OPERATE_FAIL, "直播间关闭,无法开播!");
        }
        if (!room.getTeacherPhone().equals(liveRoomDto.getUserPhone())) {
            throw new BusinessException(CodeMsg.ROOM_OPERATE_FAIL, "非指定教师用户无法直播");
        }
        return channelService.getChannelByChannelId(room.getChannelId());
    }

    @Override
    public String joinLive(LiveRoomDto liveRoomDto) {
        //校验获得
        LiveRoom room = getAndCheckByNub(liveRoomDto.getNub());
        //状态校验
        if (!WZCL.ROOM_STATE.ON.equals(room.getRoomState())) {
            throw new BusinessException(CodeMsg.ROOM_OPERATE_FAIL, "直播间关闭,无法进入直播间!");
        }
        AppUser user = userService.getAndCheckByPhone(liveRoomDto.getUserPhone());
        if (!WZCL.USER_TYPE.ADMIN.equals(user.getType()) && !StringUtils.equals(user.getPhone(), room.getTeacherPhone())) {
            //既不是系统管理员/又不是直播间的老师需要校验是否购课
            userCourseService.checkUserCourse(user.getPhone(), room.getCourseId());
        }
        return channelService.getWatchUrl(room.getChannelId(), user);
    }


    @Override
    public LiveRoom getLiveRoomById(Integer id) {
        Optional<LiveRoom> optional = roomRepository.findById(id);
        return optional.orElse(null);
    }

    @Override
    public LiveRoom getNoLiveRoom(Integer channelId) {
        return roomRepository.findByChannelIdAndRoomState(channelId, WZCL.ROOM_STATE.ON);
    }

    @Override
    public LiveRoom getEndLiveRoom(Integer channelId, String sessionId) {
        return roomRepository.findByChannelIdAndSessionIdLike(channelId, "%" + sessionId + "%");
    }

    @Override
    public LiveRoom save(LiveRoom liveRoom) {
        return roomRepository.save(liveRoom);
    }

    @Override
    public List<LiveRoom> getLiveRoomList(LiveRoomDto liveRoomDto) {
        return roomRepository.findAll(getSpecification(liveRoomDto), liveRoomDto.getSort());
    }

    @Override
    public MyPage<LiveRoom> getLiveRoomPage(LiveRoomDto liveRoomDto) {
        Page<LiveRoom> all = roomRepository.findAll(getSpecification(liveRoomDto), liveRoomDto.getPageable());
        ArrayList<LiveRoom> list = new ArrayList<>(all.getContent());
        Collections.reverse(list);
        MyPage<LiveRoom> myPage = new MyPage<>();
        myPage.setContent(list);
        myPage.setTotalElements(all.getTotalElements());
        return myPage;
    }

    @Override
    public String getPlayBack(LiveRoomDto liveRoomDto) {
        //校验获得
        LiveRoom room = getAndCheckByNub(liveRoomDto.getNub());
        //用户校验
        AppUser user = userService.getAndCheckByPhone(liveRoomDto.getUserPhone());
        if (!WZCL.USER_TYPE.ADMIN.equals(user.getType()) && !StringUtils.equals(user.getPhone(), room.getTeacherPhone())) {
            //既不是系统管理员/又不是直播间的老师需要校验是否购课
            userCourseService.checkUserCourse(user.getPhone(), room.getCourseId());
        }
        //没有直播的记录
        if (StringUtils.isBlank(room.getSessionId())) {
            return null;
        }
        List<String> sessionIdList = Arrays.asList(room.getSessionId().split(","));
        List<Playback> playBack = polyvService.getPlayBack(room.getChannelId(), sessionIdList);
        return JSON.toJSONString(playBack);
    }

    private Specification<LiveRoom> getSpecification(LiveRoomDto liveRoomDto) {
        return new Specification<LiveRoom>() {
            @Override
            public Predicate toPredicate(Root<LiveRoom> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (StringUtils.isNotBlank(liveRoomDto.getName())) {
                    predicates.add(criteriaBuilder.like(root.get("name"), "%" + liveRoomDto.getName() + "%"));
                }
                if (liveRoomDto.getCourseId() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("courseId"), liveRoomDto.getCourseId()));
                }
                if (liveRoomDto.getRoomState() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("roomState"), liveRoomDto.getRoomState()));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }

    private static String createRoomNub() {
        String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        int no = (int) ((Math.random() * 9 + 1) * 1000);
        return date + no;
    }
}
